package components;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Locale;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * A test class to test dynamic compilation API.
 * 
 */
public class PlayerCompiler {
  
    public String getClassName(String code) {
        String rv = "";

        boolean classfound = false;
        boolean extendsfound = false;

        String[] toCheck = code.split(" ");
        
     

        for (int i = 0; i < toCheck.length; i++) {
            
            if(toCheck[i].length()>0 && !toCheck[i].isEmpty() && classfound && !extendsfound)
            {
                rv = toCheck[i].trim();
            }

            if(classfound && extendsfound)
            {
                break;
            }
            classfound = toCheck[i].equals("class");
            extendsfound = toCheck[i].equals("extends");
            
        }
        
        return rv;
    }

    /**
     * Does the required object initialization and compilation.
     */
    public String Compile(String code, String outputDir) {

        String rv = "";
        String className = getClassName(code);
        
        if((new File(outputDir + "/" + className + ".class")).exists())
        {
            (new File(outputDir + "/" + className + ".class")).renameTo(new File(outputDir + "/" + className + ".class_Old"));
        }

        /* Creating dynamic java source code file object */
        SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject(className, code);

        JavaFileObject javaFileObjects[] = new JavaFileObject[] { fileObject };

        /* Instantiating the java compiler */
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        //System.out.println(compiler);

        /**
         * Retrieving the standard file manager from compiler object, which is
         * used to provide basic building block for customizing how a compiler
         * reads and writes to files.
         * 
         * The same file manager can be reopened for another compiler task. Thus
         * we reduce the overhead of scanning through file system and jar files
         * each time
         */
        StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null,
                Locale.getDefault(), null);

        /*
         * Prepare a list of compilation units (java source code file objects)
         * to input to compilation task
         */
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);

        /* Prepare any compilation options to be used during compilation */
        // In this example, we are asking the compiler to place the output files
        // under bin folder.
        String[] compileOptions = new String[] { "-d", outputDir, "-cp", GlobalConstants.PATH_DEPEND, "-verbose" };
        Iterable<String> compilationOptionss = Arrays.asList(compileOptions);

        /* Create a diagnostic controller, which holds the compilation problems */
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

       
        /*
         * Create a compilation task from compiler by passing in the required
         * input objects prepared above
         */
        CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics,
                compilationOptionss, null, compilationUnits);
        
        // Perform the compilation by calling the call method on compilerTask
        // object.
        boolean status = compilerTask.call();

        if (!status) {// If compilation error occurs
            /* Iterate through each compilation problem and print it */
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                rv = rv + String.format("Error on line %d, column %d in %s\n", diagnostic.getLineNumber(), diagnostic.getColumnNumber(),
                        diagnostic);
                
            }
        }
        try {
            stdFileManager.close();// Close the file manager
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        if(!(new File(outputDir + "/" + className + ".class")).exists())
        {
            rv += "Compile error: file not created! (check Syntax)";
            if( (new File(outputDir + "/" + className + ".class_Old")).exists() )
            {
                (new File(outputDir + "/" + className + ".class_Old")).renameTo(new File(outputDir + "/" + className + ".class"));
            }
        }
        
        rv.replace("\n", "\n<br>");
        
        return rv;

    }

}

/**
 * Creates a dynamic source code file object
 * 
 * This is an example of how we can prepare a dynamic java source code for
 * compilation. This class reads the java code from a string and prepares a
 * JavaFileObject
 * 
 */
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject {
    private String qualifiedName;

    private String sourceCode;

    /**
     * Converts the name to an URI, as that is the format expected by
     * JavaFileObject
     * 
     * 
     * @param fully
     *            qualified name given to the class file
     * @param code
     *            the source code string
     */
    protected DynamicJavaSourceCodeObject(String name, String code) {
        super(URI.create("string:///" + name.replaceAll("\\.", "/") + Kind.SOURCE.extension),
                Kind.SOURCE);
        this.qualifiedName = name;
        this.sourceCode = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
        return sourceCode;
    }
    
    @Override
    public Kind getKind() {
    	
    	return JavaFileObject.Kind.SOURCE;
    }

    public String getQualifiedName() {
        return qualifiedName;
    }

    public void setQualifiedName(String qualifiedName) {
        this.qualifiedName = qualifiedName;
    }

    public String getSourceCode() {
        return sourceCode;
    }

    public void setSourceCode(String sourceCode) {
        this.sourceCode = sourceCode;
    }
}